package net.aiion.weave;

import net.aiion.weave.asm.ClassWriter;
import net.aiion.weave.asm.Label;
import net.aiion.weave.asm.MethodVisitor;
import net.aiion.weave.asm.Opcodes;
import net.aiion.weave.asm.Type;

public class InjectingClassDump implements Opcodes
{
    public static byte[] dump() throws Exception {
        ClassWriter cw = new ClassWriter(0);
        MethodVisitor mv;

        cw.visit(V1_7, ACC_PUBLIC + ACC_SUPER, "net/aiion/weave/InjectingClass", null, "java/lang/Object",
            new String[] {
                "net/aiion/weave/injection/spi/Injector"
            });

        {
            /* private */Label nextAnnotationLabel = null;
            /* private */Label nextTargetTypeLabel = null;

            /****************************************************************************************
             * S PREPARE
             * 
             * <pre>
             * injectValueBytecodeGenerator
             * </pre>
             **/
            mv = cw.visitMethod(ACC_PUBLIC, "injectValue",
                "(Lnet/aiion/weave/injection/spi/InjectionContext;)Ljava/lang/Object;", null, null);
            mv.visitCode();

            // <<2>> = <<1>>.getTargetType()
            mv.visitVarInsn(ALOAD, 1);
            mv.visitMethodInsn(INVOKEINTERFACE, "net/aiion/weave/injection/spi/InjectionContext", "getTargetType",
                "()Ljava/lang/Class;");
            mv.visitVarInsn(ASTORE, 2);
            /**
             * E PREPARE
             ****************************************************************************************/

            //
            //

            /****************************************************************************************
             * S nth Annotation
             * 
             * <pre>
             * is1stAnnotation == injectValueByAnnotationsBytecodeGenerators.isEmpty();
             * if (!injectValueByAnnotationsBytecodeGenerators.containsKey(<<annotationName>>)) {
             *     injectValueByAnnotationsBytecodeGenerators.put(<annotationName>>, new BytecodeGenerator());
             * }
             * 
             * BytecodeGenerator a = injectValueByAnnotationsBytecodeGenerators.get(<annotationName>>)
             * </pre>
             **/

            /** if (a.isEmpty()) { */
            /** if (!is1stAnnotation) { */
            mv.visitJumpInsn(IFEQ, nextAnnotationLabel);
            mv.visitVarInsn(ALOAD, 3);
            mv.visitInsn(ARETURN);

            mv.visitLabel(nextAnnotationLabel);
            mv.visitFrame(Opcodes.F_CHOP, 1, null, 0, null);
            /** } */

            // <<1>>.hasTargetAnnotation(<<targetAnnotation>>) IFEQ <<nextAnnotationLabel>>
            nextAnnotationLabel = new Label();
            nextTargetTypeLabel = new Label();

            mv.visitVarInsn(ALOAD, 1);
            mv.visitLdcInsn(Type.getType("Lnet/aiion/weave/injection/Inject;"));
            mv.visitMethodInsn(INVOKEINTERFACE, "net/aiion/weave/injection/spi/InjectionContext",
                "hasTargetAnnotation", "(Ljava/lang/Class;)Z");
            mv.visitJumpInsn(IFEQ, nextAnnotationLabel);

            /****************************************************************************************
             * **** S 1st Type
             * 
             * <pre>
             * a
             * </pre>
             **/
            // <<3>> = this.createObject(<<1>>)
            // <<2>>.isInstance(<<3>>) IFEQ <<tryNextInjectMethodForCurrentTargetAnnotationLabel>>
            // return <<3>>
            mv.visitVarInsn(ALOAD, 0);
            mv.visitVarInsn(ALOAD, 1);
            mv.visitMethodInsn(INVOKEVIRTUAL, "net/aiion/weave/InjectingClass", "createObject",
                "(Lnet/aiion/weave/injection/spi/InjectionContext;)Ljava/lang/Object;");
            mv.visitVarInsn(ASTORE, 3);
            mv.visitVarInsn(ALOAD, 2);
            mv.visitVarInsn(ALOAD, 3);
            mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Class", "isInstance", "(Ljava/lang/Object;)Z");
            /**
             * **** E 1st Type
             ****************************************************************************************/

            /** } else { */

            /****************************************************************************************
             * **** S nth Type
             * 
             * <pre>
             * a = injectValueByAnnotationsBytecodeGenerators.get(<<annotationName>>)
             * isnthType == a != null;
             * if (!isnthType) {
             *   // Neue Annotation gefunden! Verfahren wie bei S-E 1st/nth Annotation.
             * }
             * </pre>
             **/
            mv.visitJumpInsn(IFEQ, nextTargetTypeLabel);
            mv.visitVarInsn(ALOAD, 3);
            mv.visitInsn(ARETURN);

            mv.visitLabel(nextTargetTypeLabel);
            mv.visitFrame(Opcodes.F_APPEND, 2, new Object[] {
                "java/lang/Class", "java/lang/Object"
            }, 0, null);

            // <<3>> = this.createObject2(<<1>>)
            // <<2>>.isInstance(<<3>>) IFEQ <<nextAnnotationLabel>>
            // return <<3>>
            mv.visitVarInsn(ALOAD, 0);
            mv.visitVarInsn(ALOAD, 1);
            mv.visitMethodInsn(INVOKEVIRTUAL, "net/aiion/weave/InjectingClass", "createObject2",
                "(Lnet/aiion/weave/injection/spi/InjectionContext;)Ljava/lang/Object;");
            mv.visitVarInsn(ASTORE, 3);
            mv.visitVarInsn(ALOAD, 2);
            mv.visitVarInsn(ALOAD, 3);
            mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Class", "isInstance", "(Ljava/lang/Object;)Z");
            /**
             * **** E nth Type
             ****************************************************************************************/

            /** } */

            /**
             * E nth Annotation
             ****************************************************************************************/

            //
            //

            /****************************************************************************************
             * S nth Annotation
             * 
             * <pre>
             * isnthAnnotation == !injectValueByAnnotationsBytecodeGenerators.isEmpty();
             * BytecodeGenerator a = new BytecodeGenerator();
             * injectValueByAnnotationsBytecodeGenerators.put(<<annotationName>>, a);
             * </pre>
             **/
            mv.visitJumpInsn(IFEQ, nextAnnotationLabel);
            mv.visitVarInsn(ALOAD, 3);
            mv.visitInsn(ARETURN);

            mv.visitLabel(nextAnnotationLabel);
            mv.visitFrame(Opcodes.F_CHOP, 1, null, 0, null);

            // <<1>>.hasTargetAnnotation(<<targetAnnotation>>) IFEQ <<nextAnnotationLabel>>
            nextAnnotationLabel = new Label();
            nextTargetTypeLabel = new Label();

            mv.visitVarInsn(ALOAD, 1);
            mv.visitLdcInsn(Type.getType("Ljava/lang/Deprecated;"));
            mv.visitMethodInsn(INVOKEINTERFACE, "net/aiion/weave/injection/spi/InjectionContext",
                "hasTargetAnnotation", "(Ljava/lang/Class;)Z");
            mv.visitJumpInsn(IFEQ, nextAnnotationLabel);

            /****************************************************************************************
             * **** S 1st Type
             * 
             * <pre>
             * a
             * </pre>
             **/
            // <<3>> = this.createObject3(<<1>>)
            // <<2>>.isInstance(<<3>>)
            mv.visitVarInsn(ALOAD, 0);
            mv.visitVarInsn(ALOAD, 1);
            mv.visitMethodInsn(INVOKEVIRTUAL, "net/aiion/weave/InjectingClass", "createObject3",
                "(Lnet/aiion/weave/injection/spi/InjectionContext;)Ljava/lang/Object;");
            mv.visitVarInsn(ASTORE, 3);
            mv.visitVarInsn(ALOAD, 2);
            mv.visitVarInsn(ALOAD, 3);
            mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Class", "isInstance", "(Ljava/lang/Object;)Z");
            /**
             * **** E 1st Type
             ****************************************************************************************/

            /**
             * E nth Annotation
             ****************************************************************************************/

            //
            //

            /****************************************************************************************
             * S FINISH injectValueBytecodeGenerator
             * 
             * <pre>
             * for (BytecodeGenerator a : injectValueByAnnotationsBytecodeGenerators) {
             *     a --> injectValueBytecodeGenerator
             * }
             * </pre>
             **/
            // IFEQ <<nextAnnotationLabel>>
            // return <<3>>
            mv.visitJumpInsn(IFEQ, nextAnnotationLabel);
            mv.visitVarInsn(ALOAD, 3);
            mv.visitInsn(ARETURN);

            mv.visitLabel(nextAnnotationLabel);
            mv.visitFrame(Opcodes.F_SAME, 0, null, 0, null);

            // return <<1>>.forwardInjection()
            mv.visitVarInsn(ALOAD, 1);
            mv.visitMethodInsn(INVOKEINTERFACE, "net/aiion/weave/injection/spi/InjectionContext", "forwardInjection",
                "()Ljava/lang/Object;");
            mv.visitInsn(ARETURN);

            mv.visitMaxs(2, 4);
            mv.visitEnd();
            /**
             * E FINISH
             ****************************************************************************************/
        }
        cw.visitEnd();

        return cw.toByteArray();
    }
}
